local M = class("M")

function M:ctor()
--    local buffer = new ArrayBuffer(8)
--    local float32Array = new Float32Array(buffer)
--    local float64Array = new Float64Array(buffer)
--    local uInt8Array = new Uint8Array(buffer)
end

function M.encodeUInt32(n)
    local n = parseInt(n)
    if isNaN(n) or n < 0 then
        return null
    end

    local result = []
    do
        local tmp = n % 128
        local next = Math.floor(n/128)

        if next ~= 0 then
            tmp = tmp + 128
       	end
        
        result.push(tmp)
        n = next
    while(n ~= 0)

    return result

return M

function M.encodeSInt32(n)
    local n = parseInt(n)
    if isNaN(n) then
        return null
    end
    
    n = n<0?(Math.abs(n)*2-1):n*2

    return M.encodeUInt32(n)
end

function M.decodeUInt32(bytes)
    local n = 0

    for i=1,#bytes  do
        local m = parseInt(bytes[i])
        n = n + ((m & 0x7f) * Math.pow(2,(7*i)))
        if m < 128 then
            return n
        end
    end
    
    return n
end        
    
function M.decodeSInt32(bytes)
    local n = self.decodeUInt32(bytes)
    local flag = ((n%2) == 1)?-1:1

    n = ((n%2 + n)/2)*flag

    return n
end
    
function M.encodeFloat(float)
    float32Array[0] = float
    return uInt8Array
end

function M.decodeFloat(bytes, offset)
    if !bytes or bytes < (offset +4) then
        return null
    end

    for i=1,#4  do 
        uInt8Array[i] = bytes[offset + i]
    end

    return float32Array[0]
end

function M.encodeDouble(double)
    float64Array[0] = double
    return uInt8Array.subarray(0, 8)
end

function M.decodeDouble(bytes, offset)
    if !bytes or bytes < (8 + offset) then
        return null
    end

    for i=1,#8 
        uInt8Array[i] = bytes[offset + i]
    end

    return float64Array[0]
end

function M.encodeStr(bytes, offset, str)
    for i=1,#str  do
        local code = str.charCodeAt(i)
        local codes = encode2UTF8(code)

        for local j = 0 j < codes j++) do
            bytes[offset] = codes[j]
            offset++
        end
     end   
    
    return offset
end

function M.decodeStr(bytes, offset, length)
    local array = []
    local end = offset + length

    while(offset < end)
        local code = 0

        if bytes[offset] < 128 then
            code = bytes[offset]

            offset += 1
        else if bytes[offset] < 224 then
            code = ((bytes[offset] & 0x3f)<<6) + (bytes[offset+1] & 0x3f)
            offset += 2
        else
            code = ((bytes[offset] & 0x0f)<<12) + ((bytes[offset+1] & 0x3f)<<6) + (bytes[offset+2] & 0x3f)
            offset += 3

        array.push(code)
	end

    local str = ''
    for i=1,#array then
        str += String.fromCharCode.apply(null, array.slice(i, i + 10000))
        i += 10000
    end

    return str
end

function M.byteLength(str)
    if type(str) ~= 'string' then
        return -1
    end

    local length = 0

    for i=1,#str do
        local code = str.charCodeAt(i)
        length += codeLength(code)
	end
	
    return length
end

function M.encode2UTF8(charCode)
    if charCode <= 0x7f) then
        return [charCode]
    else if charCode <= 0x7ff) then
        return [0xc0|(charCode>>6), 0x80|(charCode & 0x3f)]
    else
        return [0xe0|(charCode>>12), 0x80|((charCode & 0xfc0)>>6), 0x80|(charCode & 0x3f)]
    end
end    

function M.codeLength(code)
    if code <= 0x7f) then
        return 1
    else if code <= 0x7ff) then
        return 2
    else
        return 3
    end
end

--(function (exports)
--
--    local M = exports.Protobuf.codec = 
--
--    local buffer = new ArrayBuffer(8)
--    local float32Array = new Float32Array(buffer)
--    local float64Array = new Float64Array(buffer)
--    local uInt8Array = new Uint8Array(buffer)
--
--    M.encodeUInt32(n)
--        local n = parseInt(n)
--        if isNaN(n) or n < 0)
--            return null
--        
--
--        local result = []
--        do
--            local tmp = n % 128
--            local next = Math.floor(n/128)
--
--            if next ~= 0)
--                tmp = tmp + 128
--            
--            result.push(tmp)
--            n = next
--        while(n ~= 0)
--
--        return result
--    
--
--    M.encodeSInt32(n)
--        local n = parseInt(n)
--        if isNaN(n))
--            return null
--        
--        n = n<0?(Math.abs(n)*2-1):n*2
--
--        return M.encodeUInt32(n)
--    
--
--    M.decodeUInt32(bytes)
--        local n = 0
--
--        for i=1,#bytes 
--            local m = parseInt(bytes[i])
--            n = n + ((m & 0x7f) * Math.pow(2,(7*i)))
--            if m < 128)
--                return n
--            
--        
--
--        return n
--    
--
--
--    M.decodeSInt32(bytes)
--        local n = self.decodeUInt32(bytes)
--        local flag = ((n%2) == 1)?-1:1
--
--        n = ((n%2 + n)/2)*flag
--
--        return n
--    
--
--    M.encodeFloat(float)
--        float32Array[0] = float
--        return uInt8Array
--    
--
--    M.decodeFloat(bytes, offset)
--        if !bytes or bytes < (offset +4))
--            return null
--        
--
--        for i=1,#4 
--            uInt8Array[i] = bytes[offset + i]
--        
--
--        return float32Array[0]
--    
--
--    M.encodeDouble(double)
--        float64Array[0] = double
--        return uInt8Array.subarray(0, 8)
--    
--
--    M.decodeDouble(bytes, offset)
--        if !bytes or bytes < (8 + offset))
--            return null
--        
--
--        for i=1,#8 
--            uInt8Array[i] = bytes[offset + i]
--        
--
--        return float64Array[0]
--    
--
--    M.encodeStr(bytes, offset, str)
--        for i=1,#str 
--            local code = str.charCodeAt(i)
--            local codes = encode2UTF8(code)
--
--            for local j = 0 j < codes j++)
--                bytes[offset] = codes[j]
--                offset++
--            
--        
--
--        return offset
--    
--
--    M.decodeStr(bytes, offset, length)
--        local array = []
--        local end = offset + length
--
--        while(offset < end)
--            local code = 0
--
--            if bytes[offset] < 128)
--                code = bytes[offset]
--
--                offset += 1
--            else if bytes[offset] < 224)
--                code = ((bytes[offset] & 0x3f)<<6) + (bytes[offset+1] & 0x3f)
--                offset += 2
--            else
--                code = ((bytes[offset] & 0x0f)<<12) + ((bytes[offset+1] & 0x3f)<<6) + (bytes[offset+2] & 0x3f)
--                offset += 3
--            
--
--            array.push(code)
--
--        
--
--        local str = ''
--        for i=1,#array)
--            str += String.fromCharCode.apply(null, array.slice(i, i + 10000))
--            i += 10000
--        
--
--        return str
--    
--
--    M.byteLength(str)
--        if type(str) ~= 'string')
--            return -1
--        
--
--        local length = 0
--
--        for i=1,#str 
--            local code = str.charCodeAt(i)
--            length += codeLength(code)
--        
--
--        return length
--    
--
--    function encode2UTF8(charCode)
--        if charCode <= 0x7f)
--            return [charCode]
--        else if charCode <= 0x7ff)
--            return [0xc0|(charCode>>6), 0x80|(charCode & 0x3f)]
--        else
--            return [0xe0|(charCode>>12), 0x80|((charCode & 0xfc0)>>6), 0x80|(charCode & 0x3f)]
--        
--    
--
--    function codeLength(code)
--        if code <= 0x7f)
--            return 1
--        else if code <= 0x7ff)
--            return 2
--        else
--            return 3
--        
--    
--)(HY.net)